/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim:cindent:ts=2:et:sw=2: * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * This Original Code has been modified by IBM Corporation. Modifications made by IBM * described herein are Copyright (c) International Business Machines Corporation, 2000. * Modifications to Mozilla code or documentation identified per MPL Section 3.3 * * Date Modified by Description of modification * 04/20/2000 IBM Corp. OS/2 VisualAge build. *//* state and methods used while laying out a single line of a block frame */#ifndef nsLineLayout_h___#define nsLineLayout_h___#include"gfxTypes.h"#include"JustificationUtils.h"#include"mozilla/ArenaAllocator.h"#include"mozilla/WritingModes.h"#include"BlockReflowInput.h"#include"nsLineBox.h"classnsFloatManager;structnsStyleText;classnsLineLayout{usingBlockReflowInput=mozilla::BlockReflowInput;usingReflowInput=mozilla::ReflowInput;usingReflowOutput=mozilla::ReflowOutput;public:/** * @param aBaseLineLayout the nsLineLayout for ruby base, * nullptr if no separate base nsLineLayout is needed. */nsLineLayout(nsPresContext*aPresContext,nsFloatManager*aFloatManager,constReflowInput*aOuterReflowInput,constnsLineList::iterator*aLine,nsLineLayout*aBaseLineLayout);~nsLineLayout();voidInit(BlockReflowInput*aState,nscoordaMinLineBSize,int32_taLineNumber){mBlockRI=aState;mMinLineBSize=aMinLineBSize;mLineNumber=aLineNumber;}int32_tGetLineNumber()const{returnmLineNumber;}voidBeginLineReflow(nscoordaICoord,nscoordaBCoord,nscoordaISize,nscoordaBSize,boolaImpactedByFloats,boolaIsTopOfPage,mozilla::WritingModeaWritingMode,constnsSize&aContainerSize);voidEndLineReflow();/** * Called when a float has been placed. This method updates the * inline frame and span data to account for any change in positions * due to available space for the line boxes changing. * @param aX/aY/aWidth/aHeight are the new available * space rectangle, relative to the containing block. * @param aFloatFrame the float frame that was placed. */voidUpdateBand(mozilla::WritingModeaWM,constmozilla::LogicalRect&aNewAvailableSpace,nsIFrame*aFloatFrame);voidBeginSpan(nsIFrame*aFrame,constReflowInput*aSpanReflowInput,nscoordaLeftEdge,nscoordaRightEdge,nscoord*aBaseline);// Returns the width of the spannscoordEndSpan(nsIFrame*aFrame);// This method attaches the last frame reflowed in this line layout// to that in the base line layout.voidAttachLastFrameToBaseLineLayout(){AttachFrameToBaseLineLayout(LastFrame());}// This method attaches the root frame of this line layout to the// last reflowed frame in the base line layout.voidAttachRootFrameToBaseLineLayout(){AttachFrameToBaseLineLayout(mRootSpan->mFrame);}int32_tGetCurrentSpanCount()const;voidSplitLineTo(int32_taNewCount);boolIsZeroBSize();// Reflows the frame and returns the reflow status. aPushedFrame is true// if the frame is pushed to the next line because it doesn't fit.voidReflowFrame(nsIFrame*aFrame,nsReflowStatus&aReflowStatus,ReflowOutput*aMetrics,bool&aPushedFrame);voidAddBulletFrame(nsIFrame*aFrame,constReflowOutput&aMetrics);voidRemoveBulletFrame(nsIFrame*aFrame){PushFrame(aFrame);}/** * Place frames in the block direction (CSS property vertical-align) */voidVerticalAlignLine();boolTrimTrailingWhiteSpace();/** * Place frames in the inline direction (CSS property text-align). */voidTextAlignLine(nsLineBox*aLine,boolaIsLastLine);/** * Handle all the relative positioning in the line, compute the * combined area (== overflow area) for the line, and handle view * sizing/positioning and the setting of the overflow rect. */voidRelativePositionFrames(nsOverflowAreas&aOverflowAreas){RelativePositionFrames(mRootSpan,aOverflowAreas);}// Support methods for word-wrapping during line reflowvoidSetJustificationInfo(constmozilla::JustificationInfo&aInfo){mJustificationInfo=aInfo;}/** * @return true if so far during reflow no non-empty content has been * placed in the line (according to nsIFrame::IsEmpty()) */boolLineIsEmpty()const{returnmLineIsEmpty;}/** * @return true if so far during reflow no non-empty leaf content * (non-collapsed whitespace, replaced element, inline-block, etc) has been * placed in the line */boolLineAtStart()const{returnmLineAtStart;}boolLineIsBreakable()const;boolGetLineEndsInBR()const{returnmLineEndsInBR;}voidSetLineEndsInBR(boolaOn){mLineEndsInBR=aOn;}//----------------------------------------// Inform the line-layout about the presence of a floating frame// XXX get rid of this: use get-frame-type?boolAddFloat(nsIFrame*aFloat,nscoordaAvailableISize){// When reflowing ruby text frames, no block reflow state is// provided to the line layout. However, floats should never be// associated with ruby text containers, hence this method should// not be called in that case.MOZ_ASSERT(mBlockRI,"Should not call this method if there is no block reflow state ""available");returnmBlockRI->AddFloat(this,aFloat,aAvailableISize);}voidSetTrimmableISize(nscoordaTrimmableISize){mTrimmableISize=aTrimmableISize;}//----------------------------------------boolGetFirstLetterStyleOK()const{returnmFirstLetterStyleOK;}voidSetFirstLetterStyleOK(boolaSetting){mFirstLetterStyleOK=aSetting;}boolGetInFirstLetter()const{returnmInFirstLetter;}voidSetInFirstLetter(boolaSetting){mInFirstLetter=aSetting;}boolGetInFirstLine()const{returnmInFirstLine;}voidSetInFirstLine(boolaSetting){mInFirstLine=aSetting;}// Calling this during block reflow ensures that the next line of inlines// will be marked dirty, if there is one.voidSetDirtyNextLine(){mDirtyNextLine=true;}boolGetDirtyNextLine(){returnmDirtyNextLine;}//----------------------------------------nsPresContext*mPresContext;/** * Record where an optional break could have been placed. During line reflow, * frames containing optional break points (e.g., whitespace in text frames) * can call SetLastOptionalBreakPosition to record where a break could * have been made, but wasn't because we decided to place more content on * the line. For non-text frames, offset 0 means before the frame, offset * INT32_MAX means after the frame. * * Currently this is used to handle cases where a single word comprises * multiple frames, and the first frame fits on the line but the whole word * doesn't. We look back to the last optional break position and * reflow the whole line again, forcing a break at that position. The last * optional break position could be in a text frame or else after a frame * that cannot be part of a text run, so those are the positions we record. * * @param aFrame the frame which contains the optional break position. * * @param aFits set to true if the break position is within the available width. * * @param aPriority the priority of the break opportunity. If we are * prioritizing break opportunities, we will not set a break if we have * already set a break with a higher priority. @see gfxBreakPriority. * * @return true if we are actually reflowing with forced break position and we * should break here */boolNotifyOptionalBreakPosition(nsIFrame*aFrame,int32_taOffset,boolaFits,gfxBreakPriorityaPriority){NS_ASSERTION(!aFits||!mNeedBackup,"Shouldn't be updating the break position with a break that fits after we've already flagged an overrun");// Remember the last break position that fits; if there was no break that fit,// just remember the first breakif((aFits&&aPriority>=mLastOptionalBreakPriority)||!mLastOptionalBreakFrame){mLastOptionalBreakFrame=aFrame;mLastOptionalBreakFrameOffset=aOffset;mLastOptionalBreakPriority=aPriority;}returnaFrame&&mForceBreakFrame==aFrame&&mForceBreakFrameOffset==aOffset;}/** * Like NotifyOptionalBreakPosition, but here it's OK for mNeedBackup * to be set, because the caller is merely pruning some saved break position(s) * that are actually not feasible. */voidRestoreSavedBreakPosition(nsIFrame*aFrame,int32_taOffset,gfxBreakPriorityaPriority){mLastOptionalBreakFrame=aFrame;mLastOptionalBreakFrameOffset=aOffset;mLastOptionalBreakPriority=aPriority;}/** * Signal that no backing up will be required after all. */voidClearOptionalBreakPosition(){mNeedBackup=false;mLastOptionalBreakFrame=nullptr;mLastOptionalBreakFrameOffset=-1;mLastOptionalBreakPriority=gfxBreakPriority::eNoBreak;}// Retrieve last set optional break position. When this returns null, no// optional break has been recorded (which means that the line can't break yet).nsIFrame*GetLastOptionalBreakPosition(int32_t*aOffset,gfxBreakPriority*aPriority){*aOffset=mLastOptionalBreakFrameOffset;*aPriority=mLastOptionalBreakPriority;returnmLastOptionalBreakFrame;}// Whether any optional break position has been recorded.boolHasOptionalBreakPosition()const{returnmLastOptionalBreakFrame!=nullptr;}// Get the priority of the last optional break position recorded.gfxBreakPriorityLastOptionalBreakPriority()const{returnmLastOptionalBreakPriority;}/** * Check whether frames overflowed the available width and CanPlaceFrame * requested backing up to a saved break position. */boolNeedsBackup(){returnmNeedBackup;}// Line layout may place too much content on a line, overflowing its available// width. When that happens, if SetLastOptionalBreakPosition has been// used to record an optional break that wasn't taken, we can reflow the line// again and force the break to happen at that point (i.e., backtracking// to the last choice point).// Record that we want to break at the given content+offset (which// should have been previously returned by GetLastOptionalBreakPosition// from another nsLineLayout).voidForceBreakAtPosition(nsIFrame*aFrame,int32_taOffset){mForceBreakFrame=aFrame;mForceBreakFrameOffset=aOffset;}boolHaveForcedBreakPosition(){returnmForceBreakFrame!=nullptr;}int32_tGetForcedBreakPosition(nsIFrame*aFrame){returnmForceBreakFrame==aFrame?mForceBreakFrameOffset:-1;}/** * This can't be null. It usually returns a block frame but may return * some other kind of frame when inline frames are reflowed in a non-block * context (e.g. MathML or floating first-letter). */nsIFrame*LineContainerFrame()const{returnmBlockReflowInput->mFrame;}constReflowInput*LineContainerRI()const{returnmBlockReflowInput;}constnsLineList::iterator*GetLine()const{returnmGotLineBox?&mLineBox:nullptr;}nsLineList::iterator*GetLine(){returnmGotLineBox?&mLineBox:nullptr;}/** * Returns the accumulated advance width of frames before the current frame * on the line, plus the line container's left border+padding. * This is always positive, the advance width is measured from * the right edge for RTL blocks and from the left edge for LTR blocks. * In other words, the current frame's distance from the line container's * start content edge is: * <code>GetCurrentFrameInlineDistanceFromBlock() - lineContainer->GetUsedBorderAndPadding().left</code> * Note the use of <code>.left</code> for both LTR and RTL line containers. */nscoordGetCurrentFrameInlineDistanceFromBlock();/** * Move the inline position where the next frame will be reflowed forward by * aAmount. */voidAdvanceICoord(nscoordaAmount){mCurrentSpan->mICoord+=aAmount;}/** * Returns the writing mode for the root span. */mozilla::WritingModeGetWritingMode(){returnmRootSpan->mWritingMode;}/** * Returns the inline position where the next frame will be reflowed. */nscoordGetCurrentICoord(){returnmCurrentSpan->mICoord;}voidSetSuppressLineWrap(boolaEnabled){mSuppressLineWrap=aEnabled;}protected:// This state is constant for a given block frame doing line layout// A non-owning pointer, which points to the object owned by// nsAutoFloatManager::mNew.nsFloatManager*mFloatManager;constnsStyleText*mStyleText;// for the blockconstReflowInput*mBlockReflowInput;// The line layout for the base text. It is usually nullptr.// It becomes not null when the current line layout is for ruby// annotations. When there is nested ruby inside annotation, it// forms a linked list from the inner annotation to the outermost// line layout. The outermost line layout, which has this member// being nullptr, is responsible for managing the life cycle of// per-frame data and per-span data, and handling floats.nsLineLayout*constmBaseLineLayout;nsLineLayout*GetOutermostLineLayout(){nsLineLayout*lineLayout=this;while(lineLayout->mBaseLineLayout){lineLayout=lineLayout->mBaseLineLayout;}returnlineLayout;}nsIFrame*mLastOptionalBreakFrame;nsIFrame*mForceBreakFrame;// XXX remove this when landing bug 154892 (splitting absolute positioned frames)friendclassnsInlineFrame;// XXX Take care that nsRubyBaseContainer would give nullptr to this// member. It should not be a problem currently, since the only// code use it is handling float, which does not affect ruby.// See comment in nsLineLayout::AddFloatBlockReflowInput*mBlockRI;/* XXX hack! */nsLineList::iteratormLineBox;// Per-frame data recorded by the line-layout reflow logic. This// state is the state needed to post-process the line after reflow// has completed (block-direction alignment, inline-direction alignment,// justification and relative positioning).structPerSpanData;structPerFrameData;friendstructPerSpanData;friendstructPerFrameData;structPerFrameData{// link to next/prev frame in same spanPerFrameData*mNext;PerFrameData*mPrev;// Link to the frame of next ruby annotation. It is a linked list// through this pointer from ruby base to all its annotations. It// could be nullptr if there is no more annotation.// If PFD_ISLINKEDTOBASE is set, the current PFD is one of the ruby// annotations in the base's list, otherwise it is the ruby base,// and its mNextAnnotation is the start of the linked list.PerFrameData*mNextAnnotation;// pointer to child span data if this is an inline container framePerSpanData*mSpan;// The framensIFrame*mFrame;// From metricsnscoordmAscent;// note that mBounds is a logical rect in the *line*'s writing mode.// When setting frame coordinates, we have to convert to the frame's// writing modemozilla::LogicalRectmBounds;nsOverflowAreasmOverflowAreas;// From reflow-statemozilla::LogicalMarginmMargin;// in *line* writing modemozilla::LogicalMarginmBorderPadding;// in *line* writing modemozilla::LogicalMarginmOffsets;// in *frame* writing mode// state for text justification// Note that, although all frames would have correct inner// opportunities computed after ComputeFrameJustification, start// and end justifiable info are not reliable for non-text frames.mozilla::JustificationInfomJustificationInfo;mozilla::JustificationAssignmentmJustificationAssignment;// PerFrameData flagsboolmRelativePos:1;boolmIsTextFrame:1;boolmIsNonEmptyTextFrame:1;boolmIsNonWhitespaceTextFrame:1;boolmIsLetterFrame:1;boolmRecomputeOverflow:1;boolmIsBullet:1;boolmSkipWhenTrimmingWhitespace:1;boolmIsEmpty:1;boolmIsLinkedToBase:1;// Other state we useuint8_tmBlockDirAlign;mozilla::WritingModemWritingMode;PerFrameData*Last(){PerFrameData*pfd=this;while(pfd->mNext){pfd=pfd->mNext;}returnpfd;}boolIsStartJustifiable()const{returnmJustificationInfo.mIsStartJustifiable;}boolIsEndJustifiable()const{returnmJustificationInfo.mIsEndJustifiable;}boolParticipatesInJustification()const;};PerFrameData*mFrameFreeList;// In nsLineLayout, a "span" is a container inline frame, and a "frame" is one// of its children.//// nsLineLayout::BeginLineReflow() creates the initial PerSpanData which is// called the "root span". nsInlineFrame::ReflowFrames() creates a new// PerSpanData when it calls nsLineLayout::BeginSpan(); at this time, the// nsLineLayout object's mCurrentSpan is switched to the new span. The new// span records the old mCurrentSpan as its parent. After reflowing the child// inline frames, nsInlineFrame::ReflowFrames() calls nsLineLayout::EndSpan(),// which pops the PerSpanData and re-sets mCurrentSpan.structPerSpanData{union{PerSpanData*mParent;PerSpanData*mNextFreeSpan;};// The PerFrameData of the inline frame that "owns" the span, or null if// this is the root span. mFrame is initialized to the containing inline// frame's PerFrameData when a new PerSpanData is pushed in// nsLineLayout::BeginSpan().PerFrameData*mFrame;// The first PerFrameData structure in the span.PerFrameData*mFirstFrame;// The last PerFrameData structure in the span. PerFrameData structures are// added to the span as they are reflowed. mLastFrame may also be directly// manipulated if a line is split, or if frames are pushed from one line to// the next.PerFrameData*mLastFrame;constReflowInput*mReflowInput;boolmNoWrap;mozilla::WritingModemWritingMode;boolmContainsFloat;boolmHasNonemptyContent;nscoordmIStart;nscoordmICoord;nscoordmIEnd;nscoordmBStartLeading,mBEndLeading;nscoordmLogicalBSize;nscoordmMinBCoord,mMaxBCoord;nscoord*mBaseline;voidAppendFrame(PerFrameData*pfd){if(nullptr==mLastFrame){mFirstFrame=pfd;}else{mLastFrame->mNext=pfd;pfd->mPrev=mLastFrame;}mLastFrame=pfd;}};PerSpanData*mSpanFreeList;PerSpanData*mRootSpan;PerSpanData*mCurrentSpan;// The container size to use when converting between logical and// physical coordinates for frames in this span. For the root span// this is the size of the block cached in mContainerSize; for// child spans it's the size of the root span.nsSizeContainerSizeForSpan(PerSpanData*aPSD){return(aPSD==mRootSpan)?mContainerSize:aPSD->mFrame->mBounds.Size(mRootSpan->mWritingMode).GetPhysicalSize(mRootSpan->mWritingMode);}gfxBreakPrioritymLastOptionalBreakPriority;int32_tmLastOptionalBreakFrameOffset;int32_tmForceBreakFrameOffset;nscoordmMinLineBSize;// The amount of text indent that we applied to this line, needed for// max-element-size calculation.nscoordmTextIndent;// This state varies during the reflow of a line but is line// "global" state not span "local" state.int32_tmLineNumber;mozilla::JustificationInfomJustificationInfo;int32_tmTotalPlacedFrames;nscoordmBStartEdge;nscoordmMaxStartBoxBSize;nscoordmMaxEndBoxBSize;nscoordmInflationMinFontSize;// Final computed line-bSize value after VerticalAlignFrames for// the block has been called.nscoordmFinalLineBSize;// Amount of trimmable whitespace inline size for the trailing text// frame, if anynscoordmTrimmableISize;// Physical size. Use only for physical <-> logical coordinate conversion.nsSizemContainerSize;constnsSize&ContainerSize()const{returnmContainerSize;}boolmFirstLetterStyleOK:1;boolmIsTopOfPage:1;boolmImpactedByFloats:1;boolmLastFloatWasLetterFrame:1;boolmLineIsEmpty:1;boolmLineEndsInBR:1;boolmNeedBackup:1;boolmInFirstLine:1;boolmGotLineBox:1;boolmInFirstLetter:1;boolmHasBullet:1;boolmDirtyNextLine:1;boolmLineAtStart:1;boolmHasRuby:1;boolmSuppressLineWrap:1;int32_tmSpanDepth;#ifdef DEBUGint32_tmSpansAllocated,mSpansFreed;int32_tmFramesAllocated,mFramesFreed;#endif/** * Per span and per frame data. */mozilla::ArenaAllocator<1024,sizeof(void*)>mArena;/** * Allocate a PerFrameData from the mArena pool. The allocation is infallible. */PerFrameData*NewPerFrameData(nsIFrame*aFrame);/** * Allocate a PerSpanData from the mArena pool. The allocation is infallible. */PerSpanData*NewPerSpanData();PerFrameData*LastFrame()const{returnmCurrentSpan->mLastFrame;}/** * Unlink the given PerFrameData and all the siblings after it from * the span. The unlinked PFDs are usually freed immediately. * However, if PFD_ISLINKEDTOBASE is set, it won't be freed until * the frame of its base is unlinked. */voidUnlinkFrame(PerFrameData*pfd);/** * Free the given PerFrameData. */voidFreeFrame(PerFrameData*pfd);voidFreeSpan(PerSpanData*psd);boolInBlockContext()const{returnmSpanDepth==0;}voidPushFrame(nsIFrame*aFrame);voidAllowForStartMargin(PerFrameData*pfd,ReflowInput&aReflowInput);voidSyncAnnotationBounds(PerFrameData*aRubyFrame);boolCanPlaceFrame(PerFrameData*pfd,boolaNotSafeToBreak,boolaFrameCanContinueTextRun,boolaCanRollBackBeforeFrame,ReflowOutput&aMetrics,nsReflowStatus&aStatus,bool*aOptionalBreakAfterFits);voidPlaceFrame(PerFrameData*pfd,ReflowOutput&aMetrics);voidAdjustLeadings(nsIFrame*spanFrame,PerSpanData*psd,constnsStyleText*aStyleText,floataInflation,bool*aZeroEffectiveSpanBox);voidVerticalAlignFrames(PerSpanData*psd);voidPlaceTopBottomFrames(PerSpanData*psd,nscoordaDistanceFromStart,nscoordaLineBSize);voidApplyRelativePositioning(PerFrameData*aPFD);voidRelativePositionAnnotations(PerSpanData*aRubyPSD,nsOverflowAreas&aOverflowAreas);voidRelativePositionFrames(PerSpanData*psd,nsOverflowAreas&aOverflowAreas);boolTrimTrailingWhiteSpaceIn(PerSpanData*psd,nscoord*aDeltaISize);structJustificationComputationState;staticintAssignInterframeJustificationGaps(PerFrameData*aFrame,JustificationComputationState&aState);int32_tComputeFrameJustification(PerSpanData*psd,JustificationComputationState&aState);voidAdvanceAnnotationInlineBounds(PerFrameData*aPFD,constnsSize&aContainerSize,nscoordaDeltaICoord,nscoordaDeltaISize);voidApplyLineJustificationToAnnotations(PerFrameData*aPFD,nscoordaDeltaICoord,nscoordaDeltaISize);// Apply justification. The return value is the amount by which the width of// the span corresponding to aPSD got increased due to justification.nscoordApplyFrameJustification(PerSpanData*aPSD,mozilla::JustificationApplicationState&aState);voidExpandRubyBox(PerFrameData*aFrame,nscoordaReservedISize,constnsSize&aContainerSize);voidExpandRubyBoxWithAnnotations(PerFrameData*aFrame,constnsSize&aContainerSize);voidExpandInlineRubyBoxes(PerSpanData*aSpan);voidAttachFrameToBaseLineLayout(PerFrameData*aFrame);#ifdef DEBUGvoidDumpPerSpanData(PerSpanData*psd,int32_taIndent);#endif};#endif /* nsLineLayout_h___ */